﻿using System;

namespace StockholmsStad.Web.UI.Class.Business
{
    /// <summary>
    /// Representerar ett personnummer i GUI
    /// </summary>
    public class PersonalNumber
    {
        /// <summary>
        /// Skapa ett personnummer ifrån en sträng som är 8 eller 12 tecken.
        /// Exempel 197001015432
        /// </summary>
        /// <param name="personalNumber"></param>
        public PersonalNumber(string personalNumber) :
            this(personalNumber!=null ? personalNumber.Substring(0, 8) : null, personalNumber!=null ? personalNumber.Substring(8): null)
        {
        }

        /// <summary>
        /// Skapa ett personnummer från födelsedatum och sista nummer
        /// En person som ej är folkbokförd i Sverige har inte de sista siffrorna
        /// </summary>
        /// <param name="birthDate"></param>
        /// <param name="personIdLastNumbers"></param>
        public PersonalNumber(DateTime birthDate, int? personIdLastNumbers)
        {
            BirthDate = birthDate;
            PersonIdLastNumbers = personIdLastNumbers;
        }

        /// <summary>
        /// Skapa ett personnummer från födelsedatum och sista nummer
        /// En person som ej är folkbokförd i Sverige har inte de sista siffrorna
        /// Exempel: "19700101" och ""
        /// Exempel: "19721212" och "5432"
        /// Exempel: "19721212" och null
        /// </summary>
        /// <param name="birthDate"></param>
        /// <param name="personIdLastNumbers"></param>
        public PersonalNumber(string birthDate, string personIdLastNumbers)
        {
            BirthDateText = birthDate;
            PersonIdLastNumbersText = personIdLastNumbers;
        }

        /// <summary>
        /// Ta ut födelsedatumet
        /// </summary>
        public DateTime BirthDate { get; set; }

        /// <summary>
        /// Ta ut det sista siffrorna (kan vara null)
        /// </summary>
        public int? PersonIdLastNumbers { get; set; }

        /// <summary>
        /// Ta ut födelsedatumet som ett textsträng.
        /// Exempel: 19700101
        /// Returnerar blankt för personer med skyddad identitet
        /// </summary>
        public string BirthDateText
        {
            get { return DateTime.MinValue.Equals(BirthDate) ? "" : BirthDate.ToString("yyyyMMdd"); }
            set { BirthDate = value != null ? DateTime.ParseExact(value, "yyyyMMdd", null) : DateTime.MinValue; }
        }

        /// <summary>
        /// Ta ut de sista siffrorna eller "" om dom är null.
        /// Exempel: 0145
        /// </summary>
        public string PersonIdLastNumbersText
        {
            get { return PersonIdLastNumbers == null ? "" : PersonIdLastNumbers.Value.ToString("0000"); }
            set { SetPersonIdLastNumber(value); }
        }

        /// <summary>
        /// Returnerar true om personnummret är giltigt. Personnummer utan sista siffror
        /// eller som har 0000 är alltid giltiga
        /// </summary>
        /// <returns></returns>
        public bool IsValid()
        {
            if (PersonIdLastNumbers == null || PersonIdLastNumbers == 0)
                return true;
            string idNumber = BirthDateText.Remove(0, 2) + PersonIdLastNumbersText;

            return 0 == CalculateControlNumber(idNumber);
        }

        /// <summary>
        /// Beräkna kontrollsiffran i ett identifikationsnummer.
        /// </summary>
        /// <param name="idNumber">Id-nummer exklusive kontrollsiffra.</param>
        /// <returns>Kontrollsiffran för id-numret.</returns>
        private static int CalculateControlNumber(string idNumber)
        {
            char[] idNumberChars = idNumber.ToCharArray();
            int newNumber = 0;
            bool include = true;

            for (int charCount = 0; charCount < idNumberChars.Length; charCount++)
            {
                int number = Convert.ToInt32(idNumberChars[charCount].ToString());
                if (include)
                    number *= 2;

                include = !include;

                if (number > 9)
                    number -= 9;

                newNumber += number;
            }

            string newNumberAsString = newNumber.ToString();

            string last = newNumberAsString.Substring(newNumberAsString.Length - 1);
            int controlNumber = 10 - Convert.ToInt32(last);

            if (controlNumber == 10)
                controlNumber = 0;

            return controlNumber;
        }

        private void SetPersonIdLastNumber(string value)
        {
            if (String.IsNullOrEmpty(value))
            {
                PersonIdLastNumbers = null;
            }
            else
            {
                try
                {
                    PersonIdLastNumbers = Convert.ToInt16(value);
                }
                catch (FormatException)
                {
                    PersonIdLastNumbers = null;
                }
            }
        }

        /// <summary>
        /// Returnerar true om datum och slutsiffror är lika för ett personnummer
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public bool Equals(PersonalNumber obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            return obj.BirthDate.Equals(BirthDate) && obj.PersonIdLastNumbers.Equals(PersonIdLastNumbers);
        }

        ///<summary>
        ///
        ///                    Determines whether the specified <see cref="T:System.Object" /> is equal to the current <see cref="T:System.Object" />.
        ///                
        ///</summary>
        ///
        ///<returns>
        ///true if the specified <see cref="T:System.Object" /> is equal to the current <see cref="T:System.Object" />; otherwise, false.
        ///                
        ///</returns>
        ///
        ///<param name="obj">
        ///                    The <see cref="T:System.Object" /> to compare with the current <see cref="T:System.Object" />. 
        ///                </param>
        ///<exception cref="T:System.NullReferenceException">
        ///                    The <paramref name="obj" /> parameter is null.
        ///                </exception><filterpriority>2</filterpriority>
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != typeof (PersonalNumber)) return false;
            return Equals((PersonalNumber) obj);
        }

        ///<summary>
        ///
        ///                    Serves as a hash function for a particular type. 
        ///                
        ///</summary>
        ///
        ///<returns>
        ///
        ///                    A hash code for the current <see cref="T:System.Object" />.
        ///                
        ///</returns>
        ///<filterpriority>2</filterpriority>
        public override int GetHashCode()
        {
            unchecked
            {
                return (BirthDate.GetHashCode()*397) ^ (PersonIdLastNumbers.HasValue ? PersonIdLastNumbers.Value : 0);
            }
        }

        ///<summary>
        ///
        ///                    Returns a <see cref="T:System.String" /> that represents the current <see cref="T:System.Object" />.
        ///                
        ///</summary>
        ///
        ///<returns>
        ///
        ///                    A <see cref="T:System.String" /> that represents the current <see cref="T:System.Object" />.
        ///                
        ///</returns>
        ///<filterpriority>2</filterpriority>
        public override string ToString()
        {
            return BirthDateText + PersonIdLastNumbersText;
        }
    }
}